package com.edu.hre.db.config;

/**
 * Created by IntelliJ IDEA.
 * User: Nguyen The Tung
 * Date: Dec 26, 2009
 * Time: 11:13:28 AM
 */

import org.apache.log4j.Logger;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * DBPool contains a set of pre-establised connection to a database. Connections
 * are created when the application is inited. Number of connections is 2 Max
 * connections is 10
 */

public class DBPool {
	private static final Logger logger = Logger.getLogger(DBPool.class);

	private static LinkedList<Connection> pool = new LinkedList<Connection>();
	public final static int MAX_CONNECTIONS = 10;
	public final static int INI_CONNECTIONS = 3;

	private static boolean dbAvailable = false;

	public static boolean isDBAvailable() {
		return dbAvailable;
	}

	public static void setDBAvailable(boolean value) {
		dbAvailable = value;
	}

	static { 
		build(INI_CONNECTIONS);
	}

	public static void build(int number) {
		Connection conn = null;
		for (int i = 0; i < INI_CONNECTIONS; i++) {
			try {
				conn = makeDBConnection();
			} catch (Exception ex) {
				logger.error("build: " + ex.getMessage());
				logger.info("Can not connect database...!");
				System.exit(1);
			}
			if (conn != null) {
				pool.addLast(conn);
			}
			if (!isDBAvailable())
				setDBAvailable(true);
		}
		logger.info("OK, pool size = " + size());
	}

	public static void rebuildDBPool() {
		dbAvailable = false;
		release();
		Connection conn = null;
		boolean ok = false;
		while (!ok) {
			try {
				conn = DBPool.makeDBConnection();
				ok = true;
			} catch (SQLException e) {
				logger
						.info("Couldn't Connect to Database. Try again later in 10 seconds");
				try {
					Thread.sleep(10000);
				} catch (InterruptedException ie) {
				}
			}
		}
		if (conn != null) {
			DBPool.putConnection(conn);
		}
		if (!isDBAvailable())
			setDBAvailable(true);
		build(DBPool.INI_CONNECTIONS - 1);
	}

	public static Connection getConnection() throws Exception {
		if (!isDBAvailable())
			throw new Exception("Database is NOT available (may be down).");
		Connection conn = null;
		while (conn == null) {
			try {
				synchronized (pool) {
					if (pool.size() > 0) {
						conn = (Connection) pool.removeFirst();
						if (null != conn && conn.isClosed()) {
							logger.info("Connection is closed ...");
							rebuildDBPool();
						}

					}
				}
				if (conn == null) {
					try {
						conn = makeDBConnection();
					} catch (SQLException e) {
						logger.info("Create connection fail..");
						rebuildDBPool();
					}
				}
				if (conn != null) {
					conn.setAutoCommit(true);
				} else {
					Thread.sleep(1000);
				}

			} catch (Exception ex) {
				logger.error("getConnection: " + ex.getMessage());
			}
		}

		return conn;
	}

	public static void putConnection(Connection conn) {
		try { // Ignore closed connection
			if (conn == null || conn.isClosed()) {
				logger.info("putConnection: conn is null or closed: " + conn);
				return;
			}
			if (pool.size() >= MAX_CONNECTIONS) {
				conn.close();
				return;
			}
		} catch (SQLException ex) {
		}

		synchronized (pool) {
			pool.addLast(conn);
			pool.notify();
		}
	}

	public static void release() {
		logger.info("Closing connections in pool...");
		synchronized (pool) {
			for (Iterator<Connection> it = pool.iterator(); it.hasNext();) {
				Connection conn = (Connection) it.next();
				try {
					conn.close();
				} catch (SQLException e) {
					logger .error("release: Cannot close connection! (maybe closed?)");
				}
			}
			pool.clear();
			setDBAvailable(false);
		}
		logger.info("OK");
	}

	public static int size() {
		synchronized (pool) {
			return pool.size();
		}
	}

	public static boolean isEmpty() {
		synchronized (pool) {
			return pool.isEmpty();
		}
	}

	public void finalize() {
		release();
	}

	// --------------------------------------------------------------------------
	public static Connection makeDBConnection() throws SQLException {
		Connection conn = null;
		try {
			Class.forName(DBConfig.db_DriverClassName);
			/* System.out.println("db_DriverClassName=" +DBConfig.db_DriverClassName);
			System.out.println("db_URL=" + DBConfig.db_URL);
			System.out.println("db_user=" + DBConfig.db_user);
			System.out.println("db_password=*****"); */
			conn = DriverManager.getConnection(DBConfig.db_URL, DBConfig.db_user, DBConfig.db_password);
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new SQLException(ex.getMessage());
		}
		return conn;
	}

	public static void main(String args[]) {
		System.out.println("OK1");
		try {
			System.out.println("OK1");
		} catch (Exception ex1) {
			ex1.printStackTrace();
		}
		try {

			System.out.println(DBPool.makeDBConnection());
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public static void load() {
		System.out.println("DBPool is loaded.");
	}
}